/*********************************************************
 *
 * Filename: tx.c
 *   Author: jmdvirus
 *   Create: 2018年12月08日 星期六 11时28分45秒
 *
 *********************************************************/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

MODULE_LICENSE("GPL");

#define MEM_ALLOC_SIZE     4096
#define MEM_MAJOR 245
#define MEM_NINOR 0

char *mem_spvm;

struct cdev *mem_cdev;
struct class *mem_class;

int mem_open(struct inode *ind, struct file *filp)
{
	printk("open vmalloc start\n");
	try_module_get(THIS_MODULE);
	return 0;
}

ssize_t mem_read(struct file *filp, char *buf, size_t size, loff_t *lofp)
{
	int res = -1;
	char *tmp;
	printk("copy data out\n");
	tmp = mem_spvm;
	if (size > MEM_ALLOC_SIZE)
		size = MEM_ALLOC_SIZE;

	if (tmp != NULL) {
		res = copy_to_user(buf, tmp, size);
	}
	if (res == 0) {
		printk("copy success addr = %s\n", tmp);
		return size;
	} else {
		printk("copy failed \n");
	}
	return 0;
}

ssize_t mem_write(struct file *filp, const char *buf, size_t size, loff_t *lofp)
{
	int res = -1;
	char *tmp;
	printk("write data old, %s \n", mem_spvm);
	tmp = mem_spvm;
	if (size > MEM_ALLOC_SIZE)
		size = MEM_ALLOC_SIZE;

	if (tmp != NULL) {
		res = copy_from_user(tmp, buf, size);
	}
	if (res == 0) {
		printk("write success data = %s\n", tmp);
		return size;
	} else {
		printk("write fail\n");
	}
	return 0;
}

int mem_release(struct inode *nd, struct file *filp)
{
	printk("close\n");
	module_put(THIS_MODULE);
	return 0;
}

struct file_operations mem_fops = {
	.open = mem_open,
	.release = mem_release,
	.read = mem_read,
	.write = mem_write,
};

static int __init cdev_alloc_init(void)
{
	printk("enter cdev alloc init\n");
	int res = 0;

	int devno = MKDEV(MEM_MAJOR, MEM_NINOR);
	mem_spvm = (char*)vmalloc(MEM_ALLOC_SIZE);
	if (mem_spvm == NULL) {
		printk("vmalloc failed \n");
	} else {
		printk("mem_spvm malloc success 0x%x, %s\n", (unsigned int)mem_spvm, mem_spvm);
	}

	mem_cdev = cdev_alloc();
	if (mem_cdev == NULL) {
		printk("cdev_alloc failed\n");
		return -1;
	}
	printk("cdev alloc addr = 0x%x\n", (unsigned int)mem_cdev);
	if (&(mem_cdev->list) != NULL) {
		printk("list_head of mem_cdev has been initialized\n");
	}
	
	if (&(mem_cdev->kobj) != NULL) {
		printk("kobj.state_in_sysfs: %d, kobj.state_initialized: %d\n", mem_cdev->kobj.state_in_sysfs,
				mem_cdev->kobj.state_initialized);
	}

	cdev_init(mem_cdev, &mem_fops);
	mem_cdev->owner = THIS_MODULE;
	res = cdev_add(mem_cdev, devno, 1);
	if (res) {
		cdev_del(mem_cdev);
		mem_cdev = NULL;
		printk("cdev add Error\n");
	} else {
		printk("cdev add success\n");
	}

	printk("out the cdev alloc init\n");
	return 0;
}

static void __exit cdev_alloc_exit(void)
{
	printk("enter alloc exit\n");
	if (mem_cdev != NULL) {
		//kfree(mem_cdev);
		cdev_del(mem_cdev);
	}
	if (mem_spvm != NULL) {
		vfree(mem_spvm);
	}
	printk("out alloc exit\n");
}

module_init(cdev_alloc_init);
module_exit(cdev_alloc_exit);

